﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Mvc;
using Kendo.Mvc.UI;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Telerik.JustMock;
using TelerikedIn.Data;
using TelerikedIn.Models;
using TelerikedIn.WebApp.Areas.Administration.Controllers;
using TelerikedIn.WebApp.Areas.Administration.Models;
using Kendo.Mvc;
using Microsoft.AspNet.Identity.EntityFramework;

namespace TelerikedIn.WebApp.Tests.Controllers
{
    [TestClass]
    public class AdministrationUsersControllerTests
    {
        [TestMethod]
        public void TestGetShouldReturnProperNumberOfResults()
        {
            var controller = GetController();
            var request = new DataSourceRequest();
            var response = controller.AllUsers(request);
            var allUsers = (response.Data as DataSourceResult).Data as IEnumerable<UserDetailsVM>;

            Assert.IsNotNull(allUsers);
            Assert.AreEqual(2, allUsers.Count());
            Assert.IsInstanceOfType(allUsers.First(), typeof(UserDetailsVM));
            Assert.IsInstanceOfType(allUsers.Skip(1).First(), typeof(UserDetailsVM));
        }

        [TestMethod]
        public void TestGetShouldReturnProperResultValues()
        {
            var controller = GetController();
            var request = new DataSourceRequest();
            var response = controller.AllUsers(request);
            var allUsers = (response.Data as DataSourceResult).Data as IEnumerable<UserDetailsVM>;

            Assert.AreEqual("First", allUsers.First().FirstName);
            Assert.AreEqual("Last", allUsers.First().LastName);
            Assert.AreEqual("First", allUsers.Skip(1).First().FirstName);
            Assert.AreEqual("Last", allUsers.Skip(1).First().LastName);
            Assert.AreEqual("New York", allUsers.Skip(1).First().City);
            Assert.AreEqual("USA", allUsers.Skip(1).First().Country);
        }

        [TestMethod]
        public void TestUpdateShouldChangeFields()
        {
            var controller = GetController();
            var request = new DataSourceRequest();
            request.Aggregates = new List<AggregateDescriptor>();
            request.Filters = new List<IFilterDescriptor>();
            request.Groups = new List<GroupDescriptor>();
            request.Page = 1;
            request.PageSize = 5;
            request.Sorts = new List<SortDescriptor>();

            var userToUpdate = new UserDetailsVM()
            {
                Id = "2",
                UserName = "FullUser",
                FirstName = "NewFirst",
                LastName = "NewLast",
                City = "New York",
                Country = "USA",
                Education = "Secondary school",
                IsBanned = false
            };

            var response = controller.Update(request, userToUpdate) as JsonResult;
            var result = (response.Data as DataSourceResult).Data as List<UserDetailsVM>;

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result.First(), typeof(UserDetailsVM));
        }

        [TestMethod]
        public void TestUpdateShouldChangeAllFields()
        {
            var controller = GetController();
            var request = new DataSourceRequest();
            request.Aggregates = new List<AggregateDescriptor>();
            request.Filters = new List<IFilterDescriptor>();
            request.Groups = new List<GroupDescriptor>();
            request.Page = 1;
            request.PageSize = 5;
            request.Sorts = new List<SortDescriptor>();

            var userToUpdate = new UserDetailsVM()
            {
                Id = "2",
                UserName = "FullUser",
                FirstName = "NewFirst",
                LastName = "NewLast",
                City = "New York",
                Country = "USA",
                Education = "Secondary school",
                IsBanned = false
            };

            var response = controller.Update(request, userToUpdate) as JsonResult;
            var result = (response.Data as DataSourceResult).Data as List<UserDetailsVM>;

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result.First(), typeof(UserDetailsVM));
            Assert.AreEqual("NewFirst", result.First().FirstName);
            Assert.AreEqual("NewLast", result.First().LastName);
        }

        [TestMethod]
        public void TestUpdateShouldNotChangeNotListedFields()
        {
            var controller = GetController();
            var request = new DataSourceRequest();
            request.Aggregates = new List<AggregateDescriptor>();
            request.Filters = new List<IFilterDescriptor>();
            request.Groups = new List<GroupDescriptor>();
            request.Page = 1;
            request.PageSize = 5;
            request.Sorts = new List<SortDescriptor>();

            var userToUpdate = new UserDetailsVM()
            {
                Id = "2",
                UserName = "FullUser",
                FirstName="First",
                LastName = "NewLast",
                City = "New York",
                Country = "USA",
                Education = "Secondary school",
                IsBanned = false
            };

            var response = controller.Update(request, userToUpdate) as JsonResult;
            var result = (response.Data as DataSourceResult).Data as List<UserDetailsVM>;

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result.First(), typeof(UserDetailsVM));
            Assert.AreEqual("First", result.First().FirstName);
            Assert.AreEqual("NewLast", result.First().LastName);
        }

        [TestMethod]
        public void TestBanShouldDisableSignIn()
        {
            var controller = GetController();
            var request = new DataSourceRequest();
            request.Aggregates = new List<AggregateDescriptor>();
            request.Filters = new List<IFilterDescriptor>();
            request.Groups = new List<GroupDescriptor>();
            request.Page = 1;
            request.PageSize = 5;
            request.Sorts = new List<SortDescriptor>();

            var userToUpdate = new UserDetailsVM()
            {
                Id = "2",
                UserName = "FullUser",
                FirstName = "First",
                LastName = "NewLast",
                City = "New York",
                Country = "USA",
                Education = "Secondary school",
                IsBanned = true
            };

            var response = controller.Update(request, userToUpdate) as JsonResult;
            var result = (response.Data as DataSourceResult).Data as List<UserDetailsVM>;

            Assert.IsNotNull(result);
            Assert.IsInstanceOfType(result.First(), typeof(UserDetailsVM));
            Assert.AreEqual(true, result.First().IsBanned);
        }

        private UsersController GetController()
        {
            var users = Mock.Create<IRepository<ApplicationUser>>();
            var skills = Mock.Create<IRepository<Skill>>();
            var endorsements = Mock.Create<IRepository<Endorsment>>();
            var roles = Mock.Create<IRepository<Role>>();

            var adminRole = new Role() { Id = "1", Name = "Administrator" };
            var userRole = new Role() { Id = "2", Name = "User" };
            IQueryable<Role> rolesList = new List<Role>() { adminRole, userRole }.AsQueryable();
            Mock.Arrange(() => roles.All()).Returns(rolesList);

            var minimalUser = new ApplicationUser()
            {
                Id = "1",
                UserName = "MinimalUser",
                FirstName = "First",
                LastName = "Last",
                Management = new Microsoft.AspNet.Identity.EntityFramework.UserManagement() { DisableSignIn = false },
                Roles = new List<UserRole>() { new UserRole() { Role = adminRole } }
            };

            var fullUser = new ApplicationUser()
            {
                Id = "2",
                UserName = "FullUser",
                FirstName = "First",
                LastName = "Last",
                City = "New York",
                Country = "USA",
                Education = "Secondary school",
                Connections = new List<Connection>() 
                {
                    new Connection() 
                    { 
                        FromUserId = "2",
                        ToUserId = "1" 
                    }
                },
                Management = new Microsoft.AspNet.Identity.EntityFramework.UserManagement() { DisableSignIn = false },
                Roles = new List<UserRole>() { new UserRole() { Role = userRole } }
            };

            IQueryable<ApplicationUser> usersList = new List<ApplicationUser>() { minimalUser, fullUser }.AsQueryable();

            Mock.Arrange(() => users.All()).Returns(() => usersList);
            Mock.Arrange(() => users.GetById(Arg.IsAny<string>())).Returns((string s) => usersList.FirstOrDefault(u => u.Id == s));
            Mock.Arrange(() => users.Add(Arg.IsAny<ApplicationUser>())).DoInstead((ApplicationUser user) => usersList.ToList().Add(user));
            Mock.Arrange(() => users.Update(Arg.IsAny<ApplicationUser>())).DoInstead((ApplicationUser user) =>
            {
                var userToUpdate = usersList.FirstOrDefault(u => u.Id == user.Id);
                if (userToUpdate != null)
                {
                    usersList.ToList().Remove(userToUpdate);
                    usersList.ToList().Add(user);
                }
                else
                {
                    throw new ArgumentNullException("user");
                }
            });

            var data = Mock.Create<IUowData>();
            Mock.Arrange(() => data.Users).Returns(users);
            Mock.Arrange(() => data.Skills).Returns(skills);
            Mock.Arrange(() => data.Endorsments).Returns(endorsements);
            Mock.Arrange(() => data.Roles).Returns(roles);

            var controller = new UsersController(data);
            return controller;
        }
    }
}
